/* $Id: chat_window.cc jjs $ */
/*
   Copyright (C) 2007 by Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>
   Part of the Moros Project http://moros.sourceforge.net/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 or any
   later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/
#define DEBUG_ROUTINE "chat_window"

#include "chat_window.h"
#include "moros.h"
#include <iostream>

#include "chatview.h"
#include "question.h"
#include "line_change.h"
#include "../general/log.h"
#include "../general/util.h"

class tab {
  public:
    ChatView* t;
    Gtk::ScrolledWindow* s;
    Glib::RefPtr<Gtk::TextBuffer> b;
};

map<std::string,tab> maps;
tab *c_map;

void chat_window::handler(channel *ch) {
	while(true) {
	    if (ch->key=="member") {
	        std::string member=ch->value;
	        std::string status="unknown";
            if (!ch->next()) break;
            if (ch->key=="status") {
                status=ch->value;
            }
            members->members_show(member, status);
            if (!ch->next()) break;
	    }
        if (ch->key=="error") {
            dbg_state("Error message from chatserver %s", ch->value.c_str());
            Gtk::Widget *w=chat_book->get_nth_page(chat_book->get_current_page());
            string channel_name=chat_book->get_tab_label_text(*w);
            buffer=maps[channel_name].b;
            Gtk::TextBuffer::iterator i=buffer->get_iter_at_line(buffer->get_line_count());
            std::string res=ch->value;
            buffer->insert_with_tag(i, res, "red");
            if (!ch->next()) break;
        }
	    if (ch->key=="channel") {
	        string channel_name=ch->value;
	        if (!ch->next()) break;
            if (maps.count(channel_name)==0) {
                dbg_calls("Add new channel %s", channel_name.c_str());
                tab ntab;
                ntab.t=new ChatView();
                ntab.s=new Gtk::ScrolledWindow();
                ntab.b=ntab.t->get_buffer();
                ntab.s->add(*ntab.t);
                maps[channel_name]=ntab;
                int page=chat_book->append_page(*ntab.s, channel_name.c_str());
                chat_book->show_all_children();
                if (page<0) {
                    dbg_error("Couldn't add channel");
                } else {
                    dbg_calls("Added channel %d", page);
                }
                if (page>-1) {
                    chat_book->set_current_page(page);
                }
            }
            c_map=&maps[channel_name];
	        if (ch->key=="text") {
                Gtk::TextBuffer::iterator i=c_map->b->get_iter_at_line(c_map->b->get_line_count());
                c_map->b->insert(i, ch->value+"\n");
	        }
	    }
	    if (ch->key=="channelname") {
	        string name=ch->value;
	        int members=0;
	        string type="";
	        string status="";
	        string descr="";
	        ch->next();
	        if (ch->key=="members") {
	            members=toint(ch->value);
	            ch->next();
	        }
	        if (ch->key=="type") {
	            type=ch->value;
	            ch->next();
	        }
	        if (ch->key=="status") {
	            status=ch->value;
	            ch->next();
	        }
	        if (ch->key=="descr") {
	            descr=ch->value;
	            ch->next();
	        }
            chanlist->record(name, members, type, status, descr);
	    }
		if (!ch->next()) break;
	}
}

void chat_window::define_text(std::string tab_name) {
    tab &ntab=maps[tab_name];
	Glib::RefPtr<Gtk::TextBuffer::TagTable> tag_table = ntab.b->get_tag_table();
	Glib::RefPtr<Gtk::TextBuffer::Tag> tag = Gtk::TextBuffer::Tag::create("blue");
	tag->property_foreground() = "blue";
	tag_table->add(tag);
	tag = Gtk::TextBuffer::Tag::create("red");
	tag->property_foreground() = "red";
	tag_table->add(tag);
	ntab.t->signal_menu.connect(sigc::mem_fun(*this, &chat_window::open_context_menu));
}

chat_window::chat_window(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml> refGlade)
: Gtk::Window(cobject),
  m_refGlade(refGlade)
{
	chat_window::show();
	m_refGlade->get_widget("chat_entry", chat_entry);
	tab ntab;
	m_refGlade->get_widget_derived("chat_text", ntab.t);
	m_refGlade->get_widget("chat_scrolled", ntab.s);
	ntab.b=ntab.t->get_buffer();
	buffer=ntab.b;
	maps["general"]=ntab;
	define_text("general");
	m_refGlade->get_widget("chat_book", chat_book);
	m_refGlade->get_widget("chat_busy", chat_busy);
	m_refGlade->get_widget("chat_autoreply", chat_autoreply);
	m_refGlade->get_widget("chat_memberlist", chat_memberlist);
	m_refGlade->get_widget("chat_channellist", chat_channellist);
	m_refGlade->get_widget("chat_joinchannel", chat_joinchannel);
	m_refGlade->get_widget("chat_quit", chat_quit);
	m_refGlade->get_widget("chat_descr", chat_descr);
	m_refGlade->get_widget("chat_private", chat_private);
	m_refGlade->get_widget("chat_invitation", chat_invitation);
	m_refGlade->get_widget("chat_passwd", chat_passwd);
	m_refGlade->get_widget("chat_silence", chat_silence);
	m_refGlade->get_widget("chat_invite", chat_invite);
	m_refGlade->get_widget_derived("list_channels", chanlist);
	chanlist->hide();
	chat_ignore=new Gtk::CheckMenuItem("Ignore");
	chat_whisper=new Gtk::MenuItem("Whisper ...");
	chat_kick=new Gtk::MenuItem("Kick");
	chat_mute=new Gtk::CheckMenuItem("Mute");
	chat_moderator=new Gtk::CheckMenuItem("Moderator");
	chat_leader=new Gtk::CheckMenuItem("New Leaer");
	chat_entry->signal_activate().connect(sigc::mem_fun(*this, &chat_window::finished_entry));
	chat_busy->signal_toggle().connect(sigc::mem_fun(*this, &chat_window::busy_toggle));
	chat_autoreply->signal_activate().connect(sigc::mem_fun(*this, &chat_window::autoreply_select));
	chat_memberlist->signal_activate().connect(sigc::mem_fun(*this, &chat_window::memberlist_select));
	chat_channellist->signal_activate().connect(sigc::mem_fun(*this, &chat_window::channellist_select));
	chat_joinchannel->signal_activate().connect(sigc::mem_fun(*this, &chat_window::joinchannel_select));
	chat_quit->signal_activate().connect(sigc::mem_fun(*this, &chat_window::quit_select));
	chat_descr->signal_activate().connect(sigc::mem_fun(*this, &chat_window::descr_select));
	chat_private->signal_activate().connect(sigc::mem_fun(*this, &chat_window::private_toggle));
	chat_invitation->signal_activate().connect(sigc::mem_fun(*this, &chat_window::invitation_toggle));
	chat_passwd->signal_activate().connect(sigc::mem_fun(*this, &chat_window::passwd_select));
	chat_silence->signal_activate().connect(sigc::mem_fun(*this, &chat_window::silence_toggle));
	chat_invite->signal_activate().connect(sigc::mem_fun(*this, &chat_window::invite_select));
	chat_ignore->signal_toggle().connect(sigc::mem_fun(*this, &chat_window::ignore_toggle));
	chat_whisper->signal_activate().connect(sigc::mem_fun(*this, &chat_window::whisper_select));
	chat_kick->signal_activate().connect(sigc::mem_fun(*this, &chat_window::kick_select));
	chat_mute->signal_toggle().connect(sigc::mem_fun(*this, &chat_window::mute_toggle));
	chat_moderator->signal_activate().connect(sigc::mem_fun(*this, &chat_window::moderator_toggle));
	chat_leader->signal_activate().connect(sigc::mem_fun(*this, &chat_window::leader_toggle));
	chat_book->signal_switch_page().connect(sigc::mem_fun(*this, &chat_window::switch_page));

	m_refGlade->get_widget_derived("list_members", members);
	members->hide();

    sigc::signal<void, channel*> signal;
    signal.connect(sigc::mem_fun(*this, &chat_window::handler));
	chan=game_data->comm.client(game_data->url, 11001, signal);
	context_menu=0;
}

chat_window::~chat_window() {}

void chat_window::finished_entry() {
	if (chat_entry->get_text() != "") chan->write("mess", chat_entry->get_text());
	chan->send();
	chat_entry->set_text("");
	dbg_calls("Entry %s", chat_entry->get_text().c_str());
}

void chat_window::busy_toggle() {
    if (chat_busy->get_active()) {
        chan->write("nowhispers", cuser);
    } else {
        chan->write("whispers", cuser);
    }
    chan->send();
}

void chat_window::memberlist_select() {
    Gtk::Widget *w=chat_book->get_nth_page(chat_book->get_current_page());
    string channel_name=chat_book->get_tab_label_text(*w);
	chan->write("channel", channel_name);
	chan->send();
	members->members_clear();
	members->present();
}

void chat_window::switch_page(GtkNotebookPage* widget, guint page) {
    Gtk::Widget *w=chat_book->get_nth_page(page);
    string channel_name=chat_book->get_tab_label_text(*w);
    chan->write("join", channel_name);
	chan->send();
}

void chat_window::channellist_select() {
    chanlist->clear();
	chan->write("channels","");
    chan->send();
	chanlist->present();
}

void chat_window::joinchannel_select() {
    dbg_calls("joinchannel_select");
    sigc::signal<void, std::string> signal;
    signal.connect(sigc::mem_fun(*this, &chat_window::do_joinchannel));
    line_changer("Enter channel to join", "", signal);
}

void chat_window::do_joinchannel(std::string text) {
    chan->write("join", text);
    chan->send();
}

void chat_window::open_context_menu(GdkEventButton* event) {
    dbg_calls("open_context_menu");
    Gtk::TextBuffer::iterator i;
    Gtk::TextBuffer::iterator j;
    Gtk::Widget *w=chat_book->get_nth_page(chat_book->get_current_page());
    string channel_name=chat_book->get_tab_label_text(*w);
    tab ntab=maps[channel_name];
    ntab.t->get_iter_at_location (i, (int)event->x, (int)event->y);
    j=i;
    j.forward_to_line_end();
    i.backward_line();
    i.forward_line();
    Glib::ustring line=i.get_text(j);
    if (line=="\n") {
        i.backward_line();
        line=i.get_text(j);
    }
    unsigned int pos=line.find("> ");
    if (pos != string::npos) {
        cuser=line.substr(1, pos-1);
        if (context_menu!=0) {
            delete context_menu;
        }
        context_menu=new Gtk::Menu;
        chat_memberlabel=new Gtk::MenuItem("Member: "+cuser);
        context_menu->append(*chat_memberlabel);
        context_menu->append(*chat_ignore);
        context_menu->append(*chat_whisper);
        context_menu->append(*chat_kick);
        context_menu->append(*chat_moderator);
        context_menu->append(*chat_leader);
        context_menu->show_all();
        context_menu->popup(event->button, event->time);
        dbg_calls("Found user: %s", cuser.c_str());
    }
}

void chat_window::ignore_toggle() {
    if (chat_ignore->get_active()) {
        chan->write("ignore", cuser);
    } else {
        chan->write("unignore", cuser);
    }
    chan->send();
}

void chat_window::whisper_select() {
    sigc::signal<void, std::string> signal;
    signal.connect(sigc::mem_fun(*this, &chat_window::do_whisper));
    line_changer("Whisper to "+cuser, "", signal);
}

void chat_window::do_whisper(std::string text) {
    chan->write("whisper", cuser);
    chan->write("text", text);
    chan->send();
}

void chat_window::kick_select() {
    chan->write("kick", cuser);
    chan->send();
}

void chat_window::mute_toggle() {
    if (chat_mute->get_active()) {
        chan->write("mute", cuser);
    } else {
        chan->write("voice", cuser);
    }
    chan->send();
}

void chat_window::moderator_toggle() {
    if (chat_moderator->get_active()) {
        chan->write("mod", cuser);
    } else {
        chan->write("unmod", cuser);
    }
    chan->send();
}

void chat_window::leader_toggle() {
    if (chat_leader->get_active()) {
        sigc::signal<void> signal;
        signal.connect(sigc::mem_fun(*this, &chat_window::do_leader));
        ask_question("Sure to crown '"+cuser+"' as the new channel leader?", signal);
    }
}

void chat_window::do_leader() {
    chan->write("crown", cuser);
    chan->send();
}

void chat_window::autoreply_select() {
    sigc::signal<void, std::string> signal;
    signal.connect(sigc::mem_fun(*this, &chat_window::do_autoreply));
    line_changer("Set autoreply on whispers", "", signal);
}

void chat_window::do_autoreply(std::string text) {
    chan->write("answer", text);
    chan->send();
}

void chat_window::quit_select() {
}

void chat_window::descr_select() {
    sigc::signal<void, std::string> signal;
    signal.connect(sigc::mem_fun(*this, &chat_window::do_title));
    line_changer("Set the channel description", "", signal);
}

void chat_window::do_title(std::string text) {
    chan->write("title", text);
    chan->send();
}

void chat_window::invitation_toggle() {
    sigc::signal<void> signal;
    if (chat_invitation->get_active()) {
        signal.connect(sigc::mem_fun(*this, &chat_window::do_invitation));
        ask_question("Sure to allow people entering to read this channel?", signal);
    } else {
        signal.connect(sigc::mem_fun(*this, &chat_window::do_open));
        ask_question("Sure to allow people to join freely?", signal);
    }
}

void chat_window::private_toggle() {
    sigc::signal<void> signal;
    if (chat_private->get_active()) {
        signal.connect(sigc::mem_fun(*this, &chat_window::do_private));
        ask_question("Sure to keep people from joining this channel themselves?", signal);
    } else {
        signal.connect(sigc::mem_fun(*this, &chat_window::do_invitation));
        ask_question("Sure to allow people entering to read this channel?", signal);
    }
}

void chat_window::do_open() {
    chan->write("type", "open");
    chan->send();
}

void chat_window::do_private() {
    chan->write("type", "private");
    chan->send();
}

void chat_window::do_invitation() {
    chan->write("type", "invitation");
    chan->send();
}

void chat_window::passwd_select() {
    sigc::signal<void, std::string> signal;
    signal.connect(sigc::mem_fun(*this, &chat_window::do_passwd));
    line_changer("Set a password on the channel", "", signal);
}

void chat_window::do_passwd(std::string text) {
    chan->write("password", text); // \todo encrypt the channel password
}

void chat_window::silence_toggle() {
    if (chat_silence->get_active()) {
        chan->write("type", "silence");
    } else {
        chan->write("type", "unsilence");
    }
    chan->send();
}

void chat_window::invite_select() {
}
